/**
 * Hawk Library File Module
 * ------------------------------
 * File function module
 */

"use strict";
var filepath = require('path');
var fs       = require('fs');
var child    = require('child_process');
var crypto   = require('crypto');
var radon    = require('radon');
var util     = radon.util;
var rootRegx = /^([a-z]:\\|\/)/i;
var sysUtil = require('util');
var FileType	= require('file-type');

function File(file, check){
	this.$file = file;
	this.$path = normalize(file);
	this.$fd = null;
	if (check && !this.exists()){
		radon.throw(100022, [file]);
	}
}

function promise_wrap_factory(handler, fn, context) {
	return function () {
		var args = [].slice.call(arguments, 0);
		return util.promise(function(done, fail){
			args.push(handler.bind(null, done, fail));
			fn.apply(context, args);
		});
	};
}

// 处理node的默认参数，[err, data]
var promise_wrap = promise_wrap_factory.bind(null, function(done, fail, err, data) {
	if (err) {
		fail(err);
	}
	else
	{
		done(data);
	}
});

// 处理node的一个参数，[data]
var promise_wrap_one = promise_wrap_factory.bind(null, function(done, fail, data) {
	done(data);
});

// 处理node的三个参数，[err, data_1, data_2]，并且处理返回第三个参数data_2
var promise_wrap_three = promise_wrap_factory.bind(null, function(done, fail, err, data_1, data_2) {
	if (err) {
		fail(err);
	}
	else
	{
		done(data_2);
	}
});


var promise = {
	'stat': promise_wrap(fs.stat, fs),
	'exists': promise_wrap_one(fs.exists, fs),
	'open': promise_wrap(fs.open, fs),
	'readFile': promise_wrap(fs.readFile, fs),
	'writeFile': promise_wrap(fs.writeFile, fs),
	'rename': promise_wrap(fs.rename, fs),
	'unlink': promise_wrap(fs.unlink, fs),
	'mkdir': promise_wrap(fs.mkdir, fs),
	'readdir': promise_wrap(fs.readdir, fs),
	'rmdir': promise_wrap(fs.rmdir, fs),
	'exec': promise_wrap(child.exec, child),
	'close': promise_wrap(fs.close, fs),
	'read': promise_wrap_three(fs.read, fs)
};

module.exports = File;

File.normalize = File.prototype.normalize = normalize;


// 检查文件是否存在
File.prototype.exists = function() {
	return promise.exists(this.$path);
};

// 获取文件状态
File.prototype.stat = function(no_exception) {
	var self = this;
	return promise.stat(self.$path).catch(
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100020, [self.$file, err.message]);
			}
		}
	);
};

File.prototype.size = function(no_exception) {
	var self = this;
	return promise.stat(self.$path).then(
		function(data)
		{
			return data.size;
		},
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100020, [self.$file, err.message]);
			}
		}
	);
};

// 改名/移动文件
File.prototype.rename = util.generator(function *(dst_file, no_exception) {
	var self = this;
	// 检查创建目标文件目录
	var path = normalize(dst_file);
	if (yield promise.exists(path)) {
		radon.throw(100023, [dst_file]);
	}
	yield mkdir(filepath.dirname(dst_file), filepath.dirname(path));

	try {
		yield promise.rename(self.$path, path);
		self.$file = dst_file;
		self.$path = path;
		return true;
	}catch (e){
		if (no_exception){
			return false;
		}else {
			radon.throw(100021, [self.$file, dst_file, e.message]);
		}
	}
});

var copy = function*(src_file, dst_file, no_exception) {
	// 检查创建目标文件目录
	var path = normalize(dst_file);
	if(yield promise.exists(path)) {
		radon.throw(100023, [dst_file]);
	}
	if(!(yield promise.exists(normalize(src_file)))) {
		radon.throw(100022, [src_file]);
	}

	yield mkdir(filepath.dirname(dst_file), filepath.dirname(path));

	// 复制文件
	try {
		var src = fs.createReadStream(normalize(src_file));
		var dst = fs.createWriteStream(path);
		yield util.promise(function(done, fail) {
			src.once('error', fail);
			dst.once('error', fail);
			dst.once('finish', done);
			src.pipe(dst);
		});
		return true;
	}catch (e){
		if (no_exception){
			return false;
		}else {
			radon.throw(100027, [this.$file, dst_file, e.message]);
		}
	}

};

// 复制文件
File.prototype.copy = util.generator(function *(dst_file, no_exception) {
	return yield copy(this.$path, dst_file, no_exception);
});


// 删除文件
File.prototype.remove = function(no_exception) {
	var self = this;
	return promise.unlink(self.$path).catch(
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100026, [self.$file, err.message]);
			}
		}
	);
};

// 打开文件, 返回一个系统的File对象
File.prototype.open = function(mode, no_exception) {
	var self = this;
	return promise.open(self.$path, mode || 'r').then(
		function(data)
		{
			self.$fd = data;
			return data;
		},
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100031, [self.$file, err.message]);
			}
		}
	);
};

// 关闭文件引用
File.prototype.close = function(no_exception){
	var self = this;
	return promise.close(this.$fd).then(
		function()
		{
			self.$fd = null;
			return null;
		},
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100035, [self.$file, err.message]);
			}
		}
	);
};

// 读取文件
File.prototype.read = function(buffer, offset, length, position, no_exception){
	var self = this;
	return promise.read(this.$fd, buffer, offset, length, position).catch(
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100030, [self.$file, err.message]);
			}
		}
	);
};

// 读取整个文件内容
File.prototype.readFile = function(no_exception) {
	var self = this;
	return promise.readFile(self.$path).catch(
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100030, [self.$file, err.message]);
			}
		}
	);
};

// 按行读取整个文件
// File.prototype.readLines = function(max, no_exception) {
// 	try {
// 		return fs.readLines(this.$path, max||-1);
// 	}catch(e){
// 		if (no_exception){
// 			return false;
// 		}else {
// 			radon.throw(100030, [this.$file, e.message]);
// 		}
// 	}
// };

// 解压文件
File.prototype.unzip = util.generator(function *(dst_path, no_exception) {
	try {
		// 调用外部命令解压
		var cmd = radon.config('path.unzip_bin');
		if (!cmd){
			radon.throw(100033);
		}

		// 判断当前文件属性
		var stat = yield this.stat();
		if (!stat.isFile()){
			radon.throw(100032, [this.$file]);
		}

		// 创建解压目录
		var real_dst = normalize(dst_path);
		if (!(yield File.isDir(dst_path))){
			yield mkdir(dst_path, real_dst, 511/*0777*/);
		}

		cmd = cmd.replace('{file}', this.$path)
				 .replace('{output}', real_dst);

		try {
			yield promise.exec(cmd);
			return real_dst;
		}catch(err){
			radon.throw(100034, [cmd, err.message]);
		}
	}catch(err){
		if (no_exception){
			return false;
		}else {
			radon.throw(100020, [this.$file, err.message]);;
		}
	}
});

File.tar = util.generator(function*(path, files, no_exception) {
	var cmd = radon.config('path.tar_bin');
	if (!cmd){
		radon.throw(100036);
	}
	if (!files || !files.length) {
		radon.throw(100038);
	}

	var file_str = [];
	files.forEach(function(f_path){
		f_path = File.normalizePath(f_path);
		file_str.push(f_path);
	});

	cmd = cmd.replace("{path}", path)
			 .replace("{files}", file_str.join(' '));

	try {
		yield promise.exec(cmd);
	} catch (e) {
		if (no_exception) {
			return false;
		} else {
			radon.throw(100037);
		}
	}
});

File.mkdir = function(path, mode, no_exception){
	return mkdir(path, normalize(path), mode).catch(
		function(err)
		{
			if (no_exception){
				return false;
			}else {
				radon.throw(100020, [path, err.message]);
			}
		}
	);
};

File.rmdir = util.generator(function *(path, no_exception){
	try {
		var real = normalize(path);
		if (!(yield promise.exists(real))){
			return true;
		}
		try {
			var stat = yield promise.stat(real);
		} catch(e) {
			radon.throw(100020, [path, e.message]);
		}
		if (stat.isDirectory()){
			// 删除目录, 缓存文件和目录记录统一删除
			var queue = [];
			var dirs = [real];
			var list, name;
			while (real = dirs.shift()){
				queue.push(1,real);
				// 检查当前目录的记录
				list = yield promise.readdir(real);
				while (name = list.shift()){
					if (name == '.' || name == '..'){
						continue;
					}
					name = real + filepath.sep + name;
					//stat.dispose();
					stat = yield promise.stat(name);
					if (stat.isDirectory()){
						dirs.push(name);
						queue.push(1);
					}else {
						queue.push(0);
					}
					queue.push(name);
				}
			}
			while (queue.length > 1){
				name = queue.pop();
				let type = queue.pop();
				if(yield (promise.exists(name)))
				{
					switch (type){
						case 1:
							yield promise.rmdir(name);
							break;
						case 0:
							yield promise.unlink(name);
							break;
					}
				}
			}
		}else {
			radon.throw(100028, [path]);
		}
	}catch(err){
		if (no_exception){
			return false;
		}else {
			throw radon.throw(100020, [path, err.message]);
		}
	}
});


File.readdir = function(path, no_exception){
	return promise.readdir(normalize(path)).then(
		function(list) {
			for (var i = list.length; i > 0;){
				switch (list[--i]){
					case '.':
					case '..':
						list.splice(i, 1);
				}
			}
			return list;
		},
		function(err) {
			if (no_exception){
				return false;
			}else {
				radon.throw(100029, [path, err.message]);
			}
		}
	);
};

File.isDir = util.generator(function *(path){
	var real = normalize(path);
	if (yield promise.exists(real)){
		try {
			var stat = yield promise.stat(real);
			return stat.isDirectory();
		} catch (err) {}
	}
	return false;
});

File.exists = function(path){
	return promise.exists(normalize(path));
};

File.remove = function(path, no_exception){
	var real = normalize(path);
	return promise.unlink(real).then(
		function() {
			return true;
		},
		function(err) {
			if (no_exception){
				return false;
			} else {
				radon.throw(100026, [path, err.message]);
			}
		}
	);
};

// 计算文件hash值
File.hash = function(path) {
	var rs = fs.createReadStream(normalize(path));
	var hash = crypto.createHash('md5');
	rs.on('data', hash.update.bind(hash));

	return util.promise(function(done, fail) {
		rs.on('end', function() {
			done(hash.digest());
		});
		rs.on('error', fail);
	});
};

File.stat = function(path) {
	return promise.stat(normalize(path));
};

File.move = util.generator(function*(oldPath, newPath, no_check) {
	var self = this;
	// 检查创建目标文件目录
	var path = normalize(newPath);
	if (!no_check && (yield promise.exists(path))) {
		radon.throw(100023, [newPath]);
	}
	yield mkdir(filepath.dirname(newPath), filepath.dirname(path));
	yield promise.rename(normalize(oldPath), normalize(newPath));
});

File.copy = copy;



File.readFile = function(path,options) {
	return promise.readFile(normalize(path),options).catch(
		function(err)
		{
			radon.throw(100030, [path, err.message]);
		}
	);
};

File.writeFile = util.generator(function*(path, data, append) {
	var oPath = normalize(path);
	if ((yield promise.exists(oPath)) && !append) {
		radon.throw(100023, [path]);
	}
	yield mkdir(filepath.dirname(path), filepath.dirname(oPath));
	yield promise.writeFile(oPath, data).catch(
		function(err)
		{
			radon.throw(100030, [path, err.message]);
		}
	);
});

File.normalizePath = normalize;

File.fileType = util.generator(function*(path) {
	var buf, file;
	if(sysUtil.isString(path) || path instanceof File) {
		// 读取文件类型
		if(!(path instanceof File)) {
			file = new File(normalize(path));
		}
		else {
			file = path;
		}

		buf = new Buffer(262);
		yield file.open();
		buf = yield file.read(buf, 0, 262, 0);
		yield file.close();
	}
	else {
		buf = path;
	}

	var type = FileType(buf);
	return type && type.ext;
});


// 标准化路径地址
function normalize(file) {
	if (!rootRegx.test(file)){
		file = radon.config('path.root') + file;
	}
	return filepath.normalize(file);
}

// 递归创建目录方法
var mkdir = util.generator(function *(rel_path, path, mode) {
	if (!mode){
		mode = 511; /*0777*/
	}

	var last, stat, queue = [];
	while (path != last){
		try {
			stat = yield promise.stat(path);
			if (stat.isDirectory()){
				break;
			}else {
				radon.throw(100024, [rel_path]);
			}
		}catch(e){
			queue.push(path);
			last = path;
			path = filepath.dirname(last);
		}
	}
	try {
		while (path = queue.pop()){
		 yield	promise.mkdir(path, mode);
		}
	}catch(e){
		radon.throw(100025, [rel_path, e.message]);
	}

	return true;
});

let readLineSync = require("./readLineSync");

File.readLineSync = readLineSync;